
import java.util.Stack;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author yuri
 */
public class Nodo {

    public static int getDescendentes() {
        return descendentes;
    }

    public static void setDescendentes(int aDescendentes) {
        descendentes = aDescendentes;
    }
    private Nodo nodoPai;
    /* matriz que representa o quebra-cabecas. */
    private int[][] estado;
    private static int[][] objetivo = inicializaObjetivo(1);
    private Acao acao;
    private int custo;
    private int profundidade;
    private int posicao_x_do_zero;
    private int posicao_y_do_zero;
    private static int descendentes = 0;

    Nodo() {
    }

    Nodo(int[][] estado, boolean procuraPorZero) {
        this.estado = estado;
        if (procuraPorZero) {
            for (int i = 0; i < estado.length; i++) {
                for (int j = 0; j < estado.length; j++) {
                    if (estado[i][j] == 0) {
                        posicao_x_do_zero = i;
                        posicao_y_do_zero = j;
                        return;
                    }
                }

            }
        }
    }

    public static int[][] getObjetivo() {
        return objetivo;
    }

    public static void setObjetivo(int[][] aObjetivo) {
        objetivo = aObjetivo;
    }

    public Nodo moveEstadoZero(Nodo pai, Acao acao) {
        Nodo filho = new Nodo(new int[pai.estado.length][pai.estado.length], false);
        for (int i = 0; i < pai.estado.length; i++) {
            for (int j = 0; j < pai.estado.length; j++) {
                filho.getEstado()[i][j] = pai.getEstado()[i][j];
            }
        }
        filho.setNodoPai(pai);
        filho.setAcao(acao);
        filho.setCusto(pai.getCusto() + 1);
        filho.setProfundidade(pai.getProfundidade() + 1);
        filho.setPosicao_x_do_zero(pai.getPosicao_x_do_zero());
        filho.setPosicao_y_do_zero(pai.getPosicao_y_do_zero());
        return filho;

    }

    public Stack expandir(Nodo pai) {
        Stack sucessores = new Stack();
        //verificar se da pra mover o zero para direita
        if (pai.getPosicao_y_do_zero() + 1 < pai.getEstado().length) {
            Nodo filho = moveEstadoZero(pai, Acao.DIREITA);
            int y = filho.getEstado()[filho.getPosicao_x_do_zero()][filho.getPosicao_y_do_zero() + 1];
            filho.getEstado()[filho.getPosicao_x_do_zero()][filho.getPosicao_y_do_zero() + 1] = 0;
            filho.getEstado()[filho.getPosicao_x_do_zero()][filho.getPosicao_y_do_zero()] = y;
            filho.setPosicao_y_do_zero(filho.getPosicao_y_do_zero() + 1);
            if (pai.getNodoPai() != null) {
                if (!pai.getNodoPai().verificaIgualdadeDeEstado(filho.getEstado(), pai.getNodoPai().getEstado())) {
                    sucessores.push(filho);
                }
            }else{
                sucessores.push(filho);
            }
        }
        //verificar se da pra mover o zero para esquerda
        if (pai.getPosicao_y_do_zero() - 1 >= 0) {
            Nodo filho = moveEstadoZero(pai, Acao.ESQUERDA);
            int y = filho.getEstado()[filho.getPosicao_x_do_zero()][filho.getPosicao_y_do_zero() - 1];
            filho.getEstado()[filho.getPosicao_x_do_zero()][filho.getPosicao_y_do_zero() - 1] = 0;
            filho.getEstado()[filho.getPosicao_x_do_zero()][filho.getPosicao_y_do_zero()] = y;
            filho.setPosicao_y_do_zero(filho.getPosicao_y_do_zero() - 1);
            if (pai.getNodoPai() != null) {
                if (!pai.getNodoPai().verificaIgualdadeDeEstado(filho.getEstado(), pai.getNodoPai().getEstado())) {
                    sucessores.push(filho);
                }
            }else{
                sucessores.push(filho);
            }
        }
        //verificar se da pra mover o zero para cima
        if (pai.getPosicao_x_do_zero() - 1 >= 0) {
            Nodo filho = moveEstadoZero(pai, Acao.CIMA);
            int x = filho.getEstado()[filho.getPosicao_x_do_zero() - 1][filho.getPosicao_y_do_zero()];
            filho.getEstado()[filho.getPosicao_x_do_zero() - 1][filho.getPosicao_y_do_zero()] = 0;
            filho.getEstado()[filho.getPosicao_x_do_zero()][filho.getPosicao_y_do_zero()] = x;
            filho.setPosicao_x_do_zero(filho.getPosicao_x_do_zero() - 1);
            if (pai.getNodoPai() != null) {
                if (!pai.getNodoPai().verificaIgualdadeDeEstado(filho.getEstado(), pai.getNodoPai().getEstado())) {
                    sucessores.push(filho);
                }
            }else{
                sucessores.push(filho);
            }
        }
        //verificar se da pra mover o zero para baixo
        if (pai.getPosicao_x_do_zero() + 1 < pai.getEstado().length) {
            Nodo filho = moveEstadoZero(pai, Acao.BAIXO);
            int x = filho.getEstado()[filho.getPosicao_x_do_zero() + 1][filho.getPosicao_y_do_zero()];
            filho.getEstado()[filho.getPosicao_x_do_zero() + 1][filho.getPosicao_y_do_zero()] = 0;
            filho.getEstado()[filho.getPosicao_x_do_zero()][filho.getPosicao_y_do_zero()] = x;
            filho.setPosicao_x_do_zero(filho.getPosicao_x_do_zero() + 1);
            if (pai.getNodoPai() != null) {
                if (!pai.getNodoPai().verificaIgualdadeDeEstado(filho.getEstado(), pai.getNodoPai().getEstado())) {
                    sucessores.push(filho);
                }
            }else{
                sucessores.push(filho);
            }
        }
        setDescendentes(descendentes + sucessores.size());
        return sucessores;
    }

    /**
     * metodo que preenche a resposta certa do jogo
     * @return matriz contendo a resposta correta
     */
    public static int[][] inicializaObjetivo(int dimensao) {
        int[][] obj = new int[dimensao][dimensao];
        int cont = 0;
        for (int i = 0; i < obj.length; i++) {
            for (int j = 0; j < obj.length; j++) {
                obj[i][j] = cont++;
            }
        }
        return obj;
    }

    public boolean isObjetivo() {
        for (int i = 0; i < getObjetivo().length; i++) {
            for (int j = 0; j < getObjetivo().length; j++) {
                if (getObjetivo()[i][j] != estado[i][j]) {
                    return false;
                }
            }
        }
        return true;
    }

    public boolean verificaIgualdadeDeEstado(int[][] estado1, int[][] estado2) {
        for (int i = 0; i < estado1.length; i++) {
            for (int j = 0; j < estado1.length; j++) {
                if (estado1[i][j] != estado2[i][j]) {
                    return false;
                }
            }
        }
        return true;
    }

    /* sets e gets */
    /**
     * @return the nodoPai
     */
    public Nodo getNodoPai() {
        return nodoPai;
    }

    /**
     * @param nodoPai the nodoPai to set
     */
    public void setNodoPai(Nodo nodoPai) {
        this.nodoPai = nodoPai;
    }

    /**
     * @return the estado
     */
    public int[][] getEstado() {
        return estado;
    }

    /**
     * @param estado the estado to set
     */
    public void setEstado(int[][] estado) {
        this.estado = estado;
    }

    /**
     * @return the acao
     */
    public Acao getAcao() {
        return acao;
    }

    /**
     * @param acao the acao to set
     */
    public void setAcao(Acao acao) {
        this.acao = acao;
    }

    /**
     * @return the custo
     */
    public int getCusto() {
        return custo;
    }

    /**
     * @param custo the custo to set
     */
    public void setCusto(int custo) {
        this.custo = custo;
    }

    /**
     * @return the profundidade
     */
    public int getProfundidade() {
        return profundidade;
    }

    /**
     * @param profundidade the profundidade to set
     */
    public void setProfundidade(int profundidade) {
        this.profundidade = profundidade;
    }

    public int getPosicao_x_do_zero() {
        return posicao_x_do_zero;
    }

    public void setPosicao_x_do_zero(int posicao_x_do_zero) {
        this.posicao_x_do_zero = posicao_x_do_zero;
    }

    public int getPosicao_y_do_zero() {
        return posicao_y_do_zero;
    }

    public void setPosicao_y_do_zero(int posicao_y_do_zero) {
        this.posicao_y_do_zero = posicao_y_do_zero;
    }

    @Override
    public String toString() {
        String saida = "";
        if (nodoPai != null) {
            saida += "F" + (nodoPai.getProfundidade() + 1);
        }
        for (int i = 0; i < estado.length; i++) {
            saida += "\n";
            for (int j = 0; j < estado.length; j++) {
                saida += "\t+[x:" + i + ",y:" + j + "] " + estado[i][j];
            }
        }
        return saida;
    }
}
